package com.xcity.game.map;

import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.PriorityBlockingQueue;
import java.util.function.BiConsumer;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.alibaba.fastjson.JSON;
import com.xcity.db.entity.Config;
import com.xcity.db.entity.Mine;
import com.xcity.db.entity.Player;
import com.xcity.db.mapper.ConfigMapper;
import com.xcity.db.mapper.MineMapper;
import com.xcity.db.mapper.PlayerMapper;
import com.xcity.game.common.Cause;
import com.xcity.game.common.GameStaticConfig;
import com.xcity.game.common.MessageConst;
import com.xcity.game.common.PlayerPool;
import com.xcity.game.event.GameEvents;
import com.xcity.game.mail.MailAttach;
import com.xcity.game.mail.MailService;
import com.xcity.game.map.template.MineTypeTemplate;
import com.xcity.game.player.cache.PlayerCacheService;
import com.xcity.game.player.robot.RobotUtils;
import com.xcity.game.vip.VipTemplate;
import com.xcity.game.vip.VipUtils;
import com.xcity.pomelo.PomeloRoute;
import com.xcity.pomelo.push.SyncPush;
import com.xcity.pomelo.push.msg.MineChangePush;
import com.xcity.pomelo.push.msg.StoneLackPush;
import com.xcity.pomelo.push.sync.CustomSync;
import com.xcity.pomelo.request.MapRequest;
import com.xcity.pomelo.request.MineCloseRequest;
import com.xcity.pomelo.request.MineDetailRequest;
import com.xcity.pomelo.request.MineExtendRequest;
import com.xcity.pomelo.request.MineFindByTargetIdRequest;
import com.xcity.pomelo.request.MineFindRequest;
import com.xcity.pomelo.request.MineGetRewardRequest;
import com.xcity.pomelo.request.MineOpenRequest;
import com.xcity.pomelo.request.MineReportListRequest;
import com.xcity.pomelo.request.MineSelfRequest;
import com.xcity.pomelo.request.MineUntakeRequest;
import com.xcity.pomelo.response.HintResponse;
import com.xcity.pomelo.response.MapResponse;
import com.xcity.pomelo.response.MapResponse.MineInfo;
import com.xcity.pomelo.response.MapResponse.MineSelfInfo;
import com.xcity.pomelo.response.MineDetailResponse;
import com.xcity.pomelo.response.MineExtendResponse;
import com.xcity.pomelo.response.MineFindByTargetIdResponse;
import com.xcity.pomelo.response.MineFindResponse;
import com.xcity.pomelo.response.MineGetRewardResponse;
import com.xcity.pomelo.response.MineOpenResponse;
import com.xcity.pomelo.response.MineReportListResponse;
import com.xcity.pomelo.response.MineSelfResponse;
import com.xcity.pomelo.response.MineUntakeResponse;
import com.xcity.pomelo.response.Response;
import com.xcity.util.BackDoorHandler;
import com.xcity.util.DBUtils;
import com.xcity.util.RandomUtils;

import gnu.trove.map.hash.TIntObjectHashMap;
import naga.x.App;
import naga.x.common.Updatable;
import naga.x.event.Event;
import naga.x.event.EventListener;
import naga.x.game.time.TimeUpdater;
import naga.x.net.Session;
import naga.x.net.annotation.OPHandler;
import naga.x.net.impl.pomelo.annotation.PomeloOP;
import naga.x.service.Service;
import naga.x.service.ServiceException;
import naga.x.util.CommonUtils;
import naga.x.util.LRUCache;
import naga.x.util.MapProcedure;

/**
 * 地图服务<br/>
 * 世界地图由多张大地图组成，每张大地图容纳88名玩家，2个资源点
 * @author yang.li
 *
 */
@OPHandler
public class MapService implements Service, EventListener, Updatable{
	protected static final Logger LOG = LoggerFactory.getLogger(MapService.class);
	protected Map<Long, Player> notifyPlayers = new ConcurrentHashMap<Long, Player>();
	
	// 玩家坐标点（客户端）
	protected int[][] pos = {
			{ 8, 1012 }, { 10, 1123 }, { 14, 506 }, { 33, 855 }, { 34, 743 }, { 94, 127 }, { 107, 1067 }, { 112, 562 }, { 113, 449 }, { 129, 795 }, 
			{ 132, 275 }, { 139, 1199 }, { 154, 924 }, { 214, 506 }, { 231, 220 }, { 232, 333 }, { 241, 1142 }, { 243, 44 }, { 253, 981 }, { 255, 869 }, 
			{ 292, 705 }, { 312, 561 }, { 332, 277 }, { 340, 1198 }, { 346, 154 }, { 351, 924 }, { 417, 778 }, { 446, 97 }, { 446, 210 }, { 508, 1079 }, 
			{ 513, 834 }, { 515, 720 }, { 543, 154 }, { 604, 1133 }, { 606, 1024 }, { 614, 777 }, { 620, 313 }, { 705, 1080 }, { 721, 368/*i:38*/ }, { 723, 254 }, 
			{ 726, 654 }, { 738, 2 }, { 743, 944 }, { 820, 312 }, { 826, 711 }, { 828, 597 }, { 839, 60 }, { 840, 1156 }, { 843, 888 }, { 844, 1001 }, 
			{ 926, 653 }, { 939, 2 }, { 942, 940 }, { 954, 125 }, { 971, 513 }, { 987, 803 }, { 1055, 67 }, { 1058, 220 }, { 1071, 571 }, { 1072, 455 }, 
			{ 1087, 745 }, { 1087, 856 }, { 1156, 277 }, { 1158, 164 }, { 1171, 512 }, { 1188, 802 }, { 1206, 0 }, { 1222, 933 }, { 1233, 663 }, 
			{ 1255, 220 }, { 1264, 346 }, { 1307, 56 }, { 1310, 1155 }, { 1324, 990 }, { 1327, 877 }, { 1330, 720 }, { 1334, 606 }, { 1365, 289 }, { 1365, 404 }, 
			{ 1408, 0 }, { 1421, 933 }, { 1431, 661 }, { 1432, 129 }, { 1447, 1064 }, { 1465, 346 }, { 1471, 798 }, { 1532, 72 }, { 1532, 188 }
	};
	
	protected int[][] banPos = {
			{}, {}, {10, 14, 22, 15}, {24, 27, 32, 28}, {36, 38, 39, 43}, {54, 59, 58, 64}, {42, 48, 49, 52}, {70, 77, 78, 84}, {57, 62, 63, 69}, {29, 33, 34, 37}, {12, 18, 19, 25}
	};
	
	// 每张大地图偏移
	protected int[] mapOffset = { 1536, 1210 };
	/**
	 *  资源点坐标（客户端）
	 */
	protected int[][] resPos = {
			{ 467, 450 }, { 1073, 1077 }, {231, 275}, {446, 154}, {721, 313}, {1072, 513}, {843, 944}, {1365, 346}, {1158, 220}, {606, 1079}, {255, 924} 
	};
	/**
	 * 每个类型资源有几块矿
	 */
	protected int[] resTypeCounts = new int[]{3, 1, 3, 1};
	@Deprecated
	// 大地图x轴方向最大块数限制（y轴无限制）
	public static final int MAP_X_BLOCKS = 100;
	// 当前已使用到的大地图索引
	protected int mapIndex;
	// 当前地图可用于分配给新玩家的坐标点
	protected List<Integer> mapAvailablePoints;
	// 地图上的玩家缓存列表（key: 地图索引）
	protected Map<Integer, List<Locatable>> units = new LRUCache<Integer, List<Locatable>>(1024);
	// 资源矿点缓存列表
	protected LRUCache<Integer, List<Mine>> mines = new LRUCache<Integer, List<Mine>>(1024);
	
	protected PriorityBlockingQueue<Mine> mineQueue;//
	
	public static int SHOW_MAP_LEVEL = 20;//大于等于15级才出现在大地图
	
	public static int TAKE_MAX_DURATION = BackDoorHandler.CHEAT_ENABLE? 180 * 1000 :  GameStaticConfig.mineMaxTakeTime * 3600 * 1000;//占领时间8小时
	public static int GET_MONEY_DURATION = BackDoorHandler.CHEAT_ENABLE? 60 * 1000 : 5 * 60 * 1000;//每5分钟结算一次
	public static int CHECK_CD = 15 * 1000;
	public static int QUEUECHECK_CD = 3 * 1000;

	public static int POS_VERSION = 1;
	
	public static final int MINE_STATE_NONE = 0;
	public static final int MINE_STATE_FIGHTING = 1;
	
	public static int MINE_TAKE_TIME_LIMIT_START = GameStaticConfig.mineLimitTimeStart;
	public static int MINE_TAKE_TIME_LIMIT_END = GameStaticConfig.mineLimitTimeEnd;
	
	public static int MAX_EXTEND_COUNT = 10;
	
	public long nextCalcTime = 0L;
	public long nextQueueCheckTime = 0L;
	
	public static int MINE_REFRESH_INFO_CD = 4 * 3600 * 1000;
	
	public List<Integer> mineLimit = new ArrayList<Integer>();
	
	public static class RemovedEids{
		public List<Long> list;
		public RemovedEids(List<Long> list){
			this.list = list;
		}
	}
	
	@Override
	public void startup() throws ServiceException {
		
		Config config = DBUtils.find(Config.KEY_MAP_INDEX, ConfigMapper.class);
		if (config == null) {
			config = new Config(Config.KEY_MAP_INDEX, -1);
			DBUtils.save(config, ConfigMapper.class);
		}
		mapIndex = config.getIntValue();
		
		config = DBUtils.find(Config.KEY_MAP_AVAILABLE_POINTS, ConfigMapper.class);
		if (config == null) {
			mapAvailablePoints = newMapPoints();
			config = new Config(Config.KEY_MAP_AVAILABLE_POINTS, JSON.toJSONString(mapAvailablePoints));
			DBUtils.save(config, ConfigMapper.class);
		} else {
			mapAvailablePoints = JSON.parseArray(config.getValue(), Integer.class);
		}
		mineLimit.clear();
		TIntObjectHashMap<Integer> tmpmap = new TIntObjectHashMap<Integer>();
		for(int i = 0; i < 11; i++){
			VipTemplate vipTemp = VipUtils.getTemplate(i);
			if(vipTemp != null){
				if(!tmpmap.containsKey(vipTemp.getMineTakeLimit())){
					tmpmap.put(vipTemp.getMineTakeLimit(), i);
					mineLimit.add(i);
				}
			}
		}
		
		mineQueue = new PriorityBlockingQueue<>(10, new Comparator<Mine>() {
			@Override
			public int compare(Mine m1, Mine m2) {
				return m1.getLastTakeTime() + m1.getExtendCount() * MapService.TAKE_MAX_DURATION < m2.getLastTakeTime() + m2.getExtendCount() * MapService.TAKE_MAX_DURATION?-1:1;
			}
		});
		App.getApp().getUpdater().addAsync(this);
		App.getApp().getEventManager().register(this);
	}

	@Override
	public void shutdown() throws ServiceException {
		DBUtils.update(new Config(Config.KEY_MAP_INDEX, mapIndex), ConfigMapper.class);
		DBUtils.update(new Config(Config.KEY_MAP_AVAILABLE_POINTS, JSON.toJSONString(mapAvailablePoints)), ConfigMapper.class);

//		for(List<Mine> list : mines.values()){
//		}
		mines.forEach(new BiConsumer<Integer, List<Mine>>() {
			@Override
			public void accept(Integer t, List<Mine> list) {
				for(int i = 0, size = list.size(); i < size; i++){
					Mine mine = list.get(i);
					try {
						DBUtils.update(mine, MineMapper.class);
						LOG.info("[MINE SAVE]ID[{}]MAP[{}]POS[{}]OWNER[{}]", mine.getId(), mine.getMap(), mine.getPos(), mine.getOwnerId());
					} catch (Exception e) {
						LOG.info("[MINE SAVE ERR]ID[{}]MAP[{}]POS[{}]OWNER[{}]", mine.getId(), mine.getMap(), mine.getPos(), mine.getOwnerId());
					}
				}
			}
		});
	}

	@Override
	public String getId() {
		return getClass().getName();
	}
	
	@Override
	public int[] getEventTypes() {
		return new int[] { /*GameEvents.EVENT_PLAYER_REGISTER,*/
				GameEvents.EVENT_CHANGE_NAME,
				GameEvents.EVENT_PLAYER_LEVEL_UP,
				GameEvents.EVENT_PLAYER_LOGOUT,
				GameEvents.EVENT_PLAYER_LOGIN};
	}

	@Override
	public void handleEvent(Event event) {
		switch (event.getType()) {
			case GameEvents.EVENT_PLAYER_REGISTER:
				Player player = event.getParameter();
//				List<Locatable> list = units.get(player.getMap());
//				if (list != null) {
//					Locatable loc = new Locatable(player);
//					int[] realXY = getRealPosition(loc); // 坐标转换为真实坐标
//					loc.setX(realXY[0]);
//					loc.setY(realXY[1]);
//					list.add(loc);
//				}
				break;
			case GameEvents.EVENT_CHANGE_NAME:
				player = event.getParameter();
				List<Locatable> list = units.get(player.getMap());
				if (list != null) {
					for (int i = list.size() - 1; i >= 0; i--) {
						Locatable loc = list.get(i);
						if (loc.getId() == player.getId()) {
							loc.setName(player.getName());
							break;
						}
					}
				}
				break;
			case GameEvents.EVENT_PLAYER_LEVEL_UP:
				player = event.getParameter();
				handleLevelUp(player);
				break;
			case GameEvents.EVENT_PLAYER_LOGIN:
				player = event.getParameter();
				playerLogin(player);
				break;
			case GameEvents.EVENT_PLAYER_LOGOUT:
				player = event.getParameter();
				playerLogout(player);
				break;
		}
	}
	
	public void playerLogin(Player player){
		if(player.getMap() > mapIndex || player.getX() >= pos.length || player.getY() != POS_VERSION || player.getMap() == -1){
			if(player.getLevel() >= SHOW_MAP_LEVEL){
				int[] pis = nextPosition();
				player.setMap(pis[0]);
				player.setX(pis[1]);
				player.setY(pis[2]);
				
				//加入缓存
				List<Locatable> list = units.get(player.getMap());
				if (list != null) {
					Locatable loc = new Locatable(player);
					int[] realXY = getRealPosition(loc); // 坐标转换为真实坐标
					loc.setX(realXY[0]);
					loc.setY(realXY[1]);
					list.add(loc);
				}
			}else{
				player.setMap(-1);
			}
			player.getMines().clearMineMap();
		}else{
			findMinesByMap(player.getMap());
			
			List<Integer> removeMineId = new ArrayList<Integer>();
			for(Integer mineId : player.getMines().tryGetMineMap().keySet()){
				int map = mineId >> 8;
				int pos = mineId & 0xFF;
				List<Mine> list = findMinesByMap(map);
				for(int i = 0, size = list.size(); i < size; i++){
					Mine mine = list.get(i);
					if(pos == mine.getPos()){
						if(mine.getOwnerId() != player.getId()){
							removeMineId.add(mineId);
						}
					}
				}
			}
			for(int i = 0; i < removeMineId.size(); i++){
				player.getMines().unTakeMine(removeMineId.get(i));
			}
			
			List<Locatable> list = units.get(player.getMap());
			if(list != null){
				for(int i = 0, size = list.size(); i < size; i++){
					Locatable loc = list.get(i);
					if(loc.id == player.getId()){
						loc.level = player.getLevel();
						break;
					}
				}
			}
		}
	}
	
	public void playerLogout(Player player){
		if(notifyPlayers.containsKey(player.getId())){
			notifyPlayers.remove(player.getId());
		}
	}
	
	public void handleLevelUp(Player player){
		if(player.getMap() > mapIndex || player.getX() >= pos.length || player.getY() != POS_VERSION || player.getMap() == -1){
			if(player.getLevel() >= SHOW_MAP_LEVEL){
				int[] pis = nextPosition();
				player.setMap(pis[0]);
				player.setX(pis[1]);
				player.setY(pis[2]);
				//加入缓存
				List<Locatable> list = units.get(player.getMap());
				if (list != null) {
					Locatable loc = new Locatable(player);
					int[] realXY = getRealPosition(loc); // 坐标转换为真实坐标
					loc.setX(realXY[0]);
					loc.setY(realXY[1]);
					list.add(loc);
				}
			}else{
				player.setMap(-1);
			}
			player.getMines().clearMineMap();
		}
	}
	
	private List<Integer> newMapPoints() {
		int len = pos.length;
		List<Integer> points = new ArrayList<Integer>(len);
		for (int i = 0; i < len; i++) {
			points.add(i);
		}
		return points;
	}
	
	/**
	 * 获取一个可用的坐标点，且从可用列表中移除
	 * @return [0]: mapIndex, [1]: x, [2]: y
	 */
	public int[] nextPosition() {
		if (mapAvailablePoints.isEmpty() || mapIndex < 0) { // 当前地图点已用完，需开辟新地图
			mapAvailablePoints = newMapPoints();
			mapIndex++;
			createResPoint();
		}
		int index = CommonUtils.randomInt(mapAvailablePoints.size());
		int x = mapAvailablePoints.remove(index);
		return new int[] { mapIndex, x, POS_VERSION }; // x:映射实际坐标（客户端）, y:标记坐标版本
	}
	
	@PomeloOP(PomeloRoute.MAP)
	protected void requestUnits(Session session, MapRequest req) {
		int map;
		Integer mapX = req.getMapX();
		Integer mapY = req.getMapY();
		Player player = session.getClient();
		if (req.isNull()) { // 为空表示取自己所在大地图的数据
			map = player.getMap();
			int[] p = MapUtils.map2mapXY(map);//mapIndexToXY(map);
			mapX = p[0];
			mapY = p[1];
		} else {
			int border = MapUtils.getCircleBorder(mapX, mapY);
			if(border * border > this.mapIndex){
				session.send(new MapResponse(req.getSerial()));
				return;
			}
//			if (mapX < 0 || mapY < 0 || mapX >= MAP_X_BLOCKS) {
//				session.send(new MapResponse(req.getSerial()));
//				return;
//			}
			map = MapUtils.mapXY2map(mapX, mapY, border);//mapXYToIndex(req.getMapX(), req.getMapY());
		}
		List<Locatable> list = units.get(map);
		if (list == null) {
			Object params = DBUtils.wrapParams("map", map, "level", SHOW_MAP_LEVEL);
			list = DBUtils.selectList(PlayerMapper.STATEMENT_FIND_BY_MAP, params);
			units.put(map, list);
			boolean needAdd = true;
			for (int i = 0, size = list.size(); i < size; i++) {
				Locatable loc = list.get(i);
				int[] realXY = getRealPosition(loc); // 坐标转换为真实坐标
				loc.setX(realXY[0]);
				loc.setY(realXY[1]);
				if (loc.getId() == player.getId()) {
					needAdd = false;
				}
			}

			if(player.getMap() == map){
				if(player.getLevel() >= SHOW_MAP_LEVEL && player.getMap() <= mapIndex && player.getMap() != -1 && player.getY() == POS_VERSION){
					if(needAdd){
						Locatable loc = new Locatable(player);
						int[] realXY = getRealPosition(loc); // 坐标转换为真实坐标
						loc.setX(realXY[0]);
						loc.setY(realXY[1]);
						list.add(loc);
					}
				}
			}
		}
		if (!player.getPool().getBooleanValue(PlayerPool.PROPERTY_ROBBED_ROBOT)) { // 新手引导阶段需要加入机器人
			if(player.getMap() == MapUtils.mapXY2map(mapX, mapY)){
				list = new ArrayList<Locatable>(list);
				Locatable robot = new Locatable(RobotUtils.getDefault());
				robot.setMap(map);	
				int[] realXY = getRealPosition(robot);
				robot.setX(realXY[0]);
				robot.setY(realXY[1]);
				for (int i = 0, size = list.size(); i < size; i++) {
					Locatable loc = list.get(i);
					if (loc.getX() == robot.getX() && loc.getY() == robot.getY()) {
						// 移除与robot相同位置的玩家
						list.remove(i);
						break;
					}
				}
				// robot放在第一位
				list.add(0, robot);
			}
			player.poolSet(PlayerPool.PROPERTY_ROBBED_ROBOT, true);
		}
		
		List<Mine> mineList = findMinesByMap(map);
		List<MineInfo> mineResponseList = new ArrayList<MineInfo>();
		for(int i = 0, size = mineList.size(); i < size; i++){
			Mine mine = mineList.get(i);
			int[] pos = mine.getPos() > -1 && mine.getPos() < resPos.length? new int[]{resPos[mine.getPos()][0], resPos[mine.getPos()][1]} : new int[]{-1, -1};
			pos[0] += mapX * mapOffset[0];//mine.getPos()
			pos[1] += mapY * mapOffset[1];//mine.getPos()
			MineInfo info = new MineInfo(mine, pos);
			mineResponseList.add(info);
		}
		session.send(new MapResponse(req.getSerial(), mapX, mapY, list, mineResponseList));
	}
	
	@PomeloOP(PomeloRoute.MINE_OPEN)
	protected void requestMineOpen(Session session, MineOpenRequest req){
		Player player = session.getClient();
		if(!notifyPlayers.containsKey(player.getId())){
			notifyPlayers.put(player.getId(), player);
			notifyPlayers.size();
		}
		List<Long> list = player.getMines().getEidList();
		player.send(new MineOpenResponse(req.getSerial(), list));
	}
	
	@PomeloOP(PomeloRoute.MINE_CLOSE)
	protected void requestMineClose(Session session, MineCloseRequest req){
		Player player = session.getClient();
		if(notifyPlayers.containsKey(player.getId())){
			notifyPlayers.remove(player.getId());
		}
		player.send(new Response(req.getSerial()));
	}
	
	@PomeloOP(PomeloRoute.MINE_DETAIL)
	protected void requestMineDetail(Session session, MineDetailRequest req){
		Player player = session.getClient();
		int map = req.getMap();
		int pos = req.getPos();
		Mine mine = findMineByMapPos(map, pos);
		long now = TimeUpdater.getInstance().now();
		if(mine.getOwnerId() > 0){
			if(now > mine.getLastRefreshTime() + MINE_REFRESH_INFO_CD){//需要刷新
				Player owner = App.getApp().getServiceManager().get(PlayerCacheService.class).find(mine.getOwnerId());
				if(owner != null){
					mine.refreshOwnerInfo(owner, now);
				}
			}
		}
		player.send(new MineDetailResponse(req.getSerial(), mine, now));
	}

	@PomeloOP(PomeloRoute.MINE_SELF)
	protected void requestMineSelf(Session session, MineSelfRequest req){
		Player player = session.getClient();
		List<MineSelfInfo> selfList = new ArrayList<MineSelfInfo>();
		Map<Integer, List<Long>> mineMap = player.getMines().tryGetMineMap();
		for(int mapid : mineMap.keySet()){
			int mapindex = mapid >> 8;
			int pos = mapid & 0xFF;
			Mine mine = findMineByMapPos(mapindex, pos);
			int[] mapXY = MapUtils.map2mapXY(mapindex);//mapIndexToXY(mapindex);
			MineSelfInfo selfMine = new MineSelfInfo(mine, mapXY);
			selfList.add(selfMine);
		}
		int count = VipUtils.getTemplate(player.getVipLvl()).getMineTakeLimit();
		player.send(new MineSelfResponse(req.getSerial(), selfList, count, mineLimit));
	}
	/**
	 * 领取奖励
	 */
	@Deprecated
//	@PomeloOP(PomeloRoute.MINE_GET_REWARD)
	protected void requestMineGetReward(Session session, MineGetRewardRequest req){
		Player player = session.getClient();
		int map = req.getMap();
		int pos = req.getPos();
		Mine mine = findMineByMapPos(map, pos);
		if(mine == null){
			player.send(new HintResponse(req.getSerial(), MessageConst.MINE_NOT_EXIST));
			return;
		}
		if(player.getId() != mine.getOwnerId() || mine.getLastTakeTime() <= 0){
			player.send(new HintResponse(req.getSerial(), MessageConst.MINE_NOT_TAKEN));
			return;
		}
		long timeNow = TimeUpdater.getInstance().now();
		int count = tryGetMineReward(mine, player, timeNow, "MINE_GET", true);
		if(count <= 0){
			player.send(new HintResponse(req.getSerial(), MessageConst.MINE_NO_MINE_RES));
			return;
		}
		long newLastTakeTime = mine.getLastTakeTime() + count * TAKE_MAX_DURATION;
		if(timeNow >= newLastTakeTime && timeNow - newLastTakeTime < TAKE_MAX_DURATION){
			mine.setLastTakeTime(newLastTakeTime);
		}else{
			newLastTakeTime = timeNow;
			mine.setLastTakeTime(newLastTakeTime);
		}
		//TODO
		player.send(new MineGetRewardResponse(req.getSerial(), mine.getTypeTemp(), count));
		LOG.info("[MINE_GET]ID[{}]MAP[{}]POS[{}]TYPE[{}]NEWTIME[{}]", player.getId(), mine.getMap(), mine.getPos(), mine.getMineType(), newLastTakeTime);
	}
	/**
	 * 放弃占领 结算
	 */
	@PomeloOP(PomeloRoute.MINE_UNTAKE)
	protected void requestUnTakeMine(Session session, MineUntakeRequest req){
		Player player = session.getClient();
		int map = req.getMap();
		int pos = req.getPos();

		Mine mine = findMineByMapPos(map, pos);
		if(mine == null){
			player.send(new HintResponse(req.getSerial(), MessageConst.MINE_NOT_EXIST));
			return;
		}
		if(player.getId() != mine.getOwnerId()){
			player.send(new HintResponse(req.getSerial(), MessageConst.MINE_NOT_TAKEN));
			return;
		}
		long timeNow = TimeUpdater.getInstance().now();
		int count = tryGetMineReward(mine, player, timeNow, Cause.MINE_UNTAKE, false);
//		if(count > 0){
//		}
		mine.setLastTakeTime(0);
		mine.setOwnerId(0);
		mine.setTakeTime(0);
		mine.setExtendCount((byte) 0);
		List<Long> removeList = player.getMines().getEidsByMineId(mine.getId());
		player.getMines().unTakeMine(mine.getId());
		player.send(new MineUntakeResponse(req.getSerial(), mine, mine.getTypeTemp(), count, removeList));
		LOG.info("[MINE_UNTAKE]ID[{}]MAP[{}]POS[{}]TYPE[{}]", player.getId(), mine.getMap(), mine.getPos(), mine.getMineType());
		for (Player ply : notifyPlayers.values()) {
			ply.send(new MineChangePush(mine));
			//LOG.info("[MINEPUSH]PID[{}]NAME[{}]", ply.getId(), ply.getName());
		}
	}
	
	/**
	 * 请求延长
	 */
	@PomeloOP(PomeloRoute.MINE_EXTEND)
	protected void requestExtendTakeMine(Session session, MineExtendRequest req){
		Player player = session.getClient();
		int map = req.getMap();
		int pos = req.getPos();
		
		Mine mine = findMineByMapPos(map, pos);
		if(mine == null){
			player.send(new HintResponse(req.getSerial(), MessageConst.MINE_NOT_EXIST));
			return;
		}
		if(player.getId() != mine.getOwnerId()){
			player.send(new HintResponse(req.getSerial(), MessageConst.MINE_NOT_TAKEN));
			return;
		}
		int curCount = mine.getExtendCount();
		if(curCount > MAX_EXTEND_COUNT){
			player.send(new HintResponse(req.getSerial(), MessageConst.MINE_MAX_EXTEND));
			return;
		}
		if(mine.getTypeTemp().getCostStone() > 0 && player.getStone() < mine.getTypeTemp().getCostStone()){
			player.send(StoneLackPush.DEFAULT);
			player.send(new HintResponse(req.getSerial(), MessageConst.MINE_NO_STONE));
			return;
		}
		if(mine.getTypeTemp().getCostStr() > 0 && player.getStrength() < mine.getTypeTemp().getCostStr()){
			player.send(new HintResponse(req.getSerial(), MessageConst.MINE_NO_STR));
			return;
		}
		mine.setExtendCount((byte) (curCount + 1));
		if(mine.getTypeTemp().getCostStone() > 0){
			player.costStone(mine.getTypeTemp().getCostStone(), Cause.MINE_EXTEND);
		}
		if(mine.getTypeTemp().getCostStr() > 0){
			player.decStrength(mine.getTypeTemp().getCostStr(), Cause.MINE_EXTEND);
		}
		player.send(new MineExtendResponse(req.getSerial(), mine));
		LOG.info("[MINE_EXTEND]ID[{}]MAP[{}]POS[{}]TYPE[{}]", player.getId(), mine.getMap(), mine.getPos(), mine.getMineType());
	}

	/**
	 * 找到对应矿
	 */
	@PomeloOP(PomeloRoute.MINE_FIND)
	protected void requestFindMine(Session session, MineFindRequest req){
		Player player = session.getClient();
		int type = req.getMineType();
		int retMap = -1;
		int retPos = 0;
		List<Mine> empty = new ArrayList<Mine>();
		List<Mine> other = new ArrayList<Mine>();
//		for(List<Mine> list : mines.values()){
//			if(retMap > -1){
//				break;
//			}
//			for(int i = 0, size = list.size(); i < size; i++){
//				Mine mine = list.get(i);
//				if(mine.getMineType() == type){
//					if(mine.getOwnerId() == 0){
//						empty.add(mine);
//						if(empty.size() > 9){
//							break;
//						}
//					}else if(mine.getOwnerId() != player.getId()){
//						other.add(mine);
//						if(other.size() > 9){
//							break;
//						}
//					}
//				}
//			}
//		}
		mines.forEach(new MapProcedure<Integer, List<Mine>>() {
			@Override
			public boolean execute(Integer t, List<Mine> list) {
				if(empty.size() > 9){
					return false;
				}
				if(other.size() > 9){
					return false;
				}
				for(int i = 0, size = list.size(); i < size; i++){
					Mine mine = list.get(i);
					if(mine.getMineType() == type){
						if(mine.getOwnerId() == 0){
							empty.add(mine);
							if(empty.size() > 9){
								break;
							}
						}else if(mine.getOwnerId() != player.getId()){
							other.add(mine);
							if(other.size() > 9){
								break;
							}
						}
					}
				}
				return true;
			}
		});
		if(empty.size() > 0){
			int index = RandomUtils.getRandomNum(0, empty.size() - 1);
			retMap = empty.get(index).getMap();
			retPos = empty.get(index).getPos();
		}else if(other.size() > 0){
			int index = RandomUtils.getRandomNum(0, other.size() - 1);
			retMap = other.get(index).getMap();
			retPos = other.get(index).getPos();
		}
		int mapX = -1;
		int mapY = -1;
		if(retMap > -1){
			int[] xy = MapUtils.map2mapXY(retMap);
			mapX = xy[0];//retMap % MAP_X_BLOCKS;
			mapY = xy[1];//retMap / MAP_X_BLOCKS;
		}
		player.send(new MineFindResponse(req.getSerial(), retMap, retPos, mapX, mapY));
	}

	/**
	 * 请求战报列表
	 */
	@PomeloOP(PomeloRoute.MINE_REPORT_LIST)
	protected void requestMineReport(Session session, MineReportListRequest req){
		Player player = session.getClient();
		player.send(new MineReportListResponse(req.getSerial(), player));
	}
	
	/**
	 * 根据ID查找对应矿
	 */
	@PomeloOP(PomeloRoute.MINE_FIND_BY_ID)
	protected void requestMineFindByTargetId(Session session, MineFindByTargetIdRequest req){
		Player player = session.getClient();
		long targetId = req.getId();
		List<Mine> list = findMinesById(targetId);
		int map = -1;
		int pos = 0;
		if(list.size() > 0){
			int index = RandomUtils.getRandomNum(0, list.size() - 1);
			Mine mine = list.get(index);
			map = mine.getMap();
			pos = mine.getPos();
		}
		int[] xy = MapUtils.map2mapXY(map);
		int mapX = xy[0];//map % MAP_X_BLOCKS;
		int mapY = xy[1];//map / MAP_X_BLOCKS;
		player.send(new MineFindByTargetIdResponse(req.getSerial(), map, pos, mapX, mapY));
	}

	@Deprecated
	private static int mapXYToIndex(int x, int y) {
		return x + y * MAP_X_BLOCKS;
	}
	
	@Deprecated
	private static int[] mapIndexToXY(int index) {
		int x = index % MAP_X_BLOCKS;
		int y = index / MAP_X_BLOCKS;
		return new int[] { x, y };
	}
	
	public int[] getRealPosition(Locatable unit) {
		int index = unit.getX();
		if (index >= 0 && index < pos.length) {
			int[] _pos = pos[index];
			if (unit.getMap() == 0) {
				return _pos;
			}
			int[] mapPos = MapUtils.map2mapXY(unit.getMap());//mapIndexToXY(unit.getMap()); // 大地图坐标
			int x = _pos[0] + mapPos[0] * mapOffset[0];
			int y = _pos[1] + mapPos[1] * mapOffset[1];
			return new int[] { x, y };
		}
		return new int[] { unit.getX(), unit.getY() };
	}
	
	/**
	 * 生成资源点
	 */
	public void createResPoint(){
		DBUtils.delete(MineMapper.STATEMENT_DELETE_MINES_BY_MAP, mapIndex);
		if(mapIndex == 0){
			mines.clear();
		}
		List<Integer> typeList = new ArrayList<Integer>();//index2type [1, 2, 2, 2, 3, 4, 4, 4]
		for(int i = 0;i < resTypeCounts.length; i++){
			for(int j = 0; j < resTypeCounts[i]; j++){
				typeList.add(i + 1);
			}
		}
		List<Mine> list = new ArrayList<Mine>();
		mines.put(mapIndex, list);
		//先随机两块固定矿(因为两块矿无法分配给玩家)
		int index = CommonUtils.randomInt(typeList.size());
		Mine mine = new Mine(mapIndex, 0, typeList.get(index));
		typeList.remove(index);
		list.add(mine);
		DBUtils.save(mine, MineMapper.class);
		
		index = CommonUtils.randomInt(typeList.size());
		mine = new Mine(mapIndex, 1, typeList.get(index));
		typeList.remove(index);
		list.add(mine);
		DBUtils.save(mine, MineMapper.class);
		//后面把typeList.get(i)类型的矿点放置在随机位置
		List<Integer> resPosList = new ArrayList<Integer>();
		for(int i = 0; i < resPos.length; i++){
			resPosList.add(i);
		}
		int size = typeList.size();//此时size是6
		for(int i = 0; i < size; i++){
			int ranindex = CommonUtils.randomInt(2, resPosList.size() - 1);//坐标index
			int resPosIndex = resPosList.get(ranindex);
			mine = new Mine(mapIndex, resPosIndex, typeList.get(i));
			resPosList.remove(ranindex);
			list.add(mine);
			DBUtils.save(mine, MineMapper.class);
			//把对应员工可用点移除
			for(int j = 0;j < banPos[resPosIndex].length; j++){
				int availableCount = mapAvailablePoints.size();
				for(int k = 0; k < availableCount; k++){
					if(mapAvailablePoints.get(k) == banPos[resPosIndex][j]){
						mapAvailablePoints.remove(k);
						break;
					}
				}
			}
		}
//		//---test
//		for(int i = 0; i < list.size(); i++){
//			Mine testmine = list.get(i);
//			System.out.println("[mine][id]" + testmine.getId() + "[map]"
//					+ testmine.getMap() + "[posindex]" + testmine.getPos() + "[type]" + testmine.getMineType());
//		}
//		
//		for(int i = 0; i < mapAvailablePoints.size(); i++){
//			System.out.print(mapAvailablePoints.get(i));
//			System.out.print(",");
//		}
//		System.out.println("-----testend-----");
//		//---testend
	}
	
	public Mine findMineByMapPos(int map, int pos){
		List<Mine> list = findMinesByMap(map);
		for(int i = 0, size = list.size(); i < size; i++){
			if(list.get(i).getPos() == pos){
				return list.get(i);
			}
		}
		return null;
	}

	public List<Mine> findMinesByMap(int map){
		List<Mine> list = mines.get(map);
		if(list == null){
			list = DBUtils.selectList(MineMapper.STATEMENT_FIND_MINES_BY_MAP, map);
			mines.put(map, list);
			//新的list判断过期
			long time = TimeUpdater.getInstance().now();
			for(int i = 0, size = list.size(); i < size; i++){
				Mine mine = list.get(i);
				if(mine.getOwnerId() > 0){
					if(mine.getLastTakeTime() + mine.getExtendCount() * TAKE_MAX_DURATION < time){
						App.getApp().getThreadPool().execute(new Runnable() {
							@Override
							public void run() {
								Player player = App.getApp().getServiceManager().get(PlayerCacheService.class).find(mine.getOwnerId());
								if(player != null){
									tryGetMineReward(mine, player, time, "MINE_FIND", true);
									List<Long> removeList = null;
									if(notifyPlayers.containsKey(player.getId())){
										removeList = player.getMines().getEidsByMineId(mine.getId());
									}
									player.getMines().unTakeMine(mine.getId());
									if(removeList != null){
										player.send(new CustomSync(SyncPush.SYNC_EMPLOYEE_UNTAKE, new RemovedEids(removeList)));
									}
									mine.end(null, 0, 0);
									mineQueue.remove(mine);
								}
							} 
						});
					}else{
						mineQueue.put(mine);
//						App.getApp().getThreadPool().execute(new Runnable() {
//							@Override
//							public void run() {
//								Player player = App.getApp().getServiceManager().get(PlayerCacheService.class).find(mine.getOwnerId());
//								if(player != null){
//									mine.end(null, 0, 0);
//								}
//							} 
//						});
					}
				}
			}
		}
		return list;
	}
	
	/**
	 * 根据玩家ID获得已占矿的list
	 * @param pid
	 * @return
	 */
	public List<Mine> findMinesById(long pid){
		List<Mine> retList = new ArrayList<Mine>();
		Player player = App.getApp().getServiceManager().get(PlayerCacheService.class).find(pid);
		if(player == null){
			return retList;
		}
		Map<Integer, List<Long>> mineMap = player.getMines().tryGetMineMap();
		for(int mapId : mineMap.keySet()){
			int map = mapId >> 8;
			int pos = mapId & 0xFF;
			Mine mine = findMineByMapPos(map, pos);
			if(mine != null){
				retList.add(mine);
			}
		}
		return retList;
	}
	
	/**
	 * 处理结束战斗
	 * @param mine
	 * @param source
	 * @param target
	 * @param time
	 */
	public void handleMineBattleEnd(Mine mine, Player source, Player target, long time, boolean isSourceWin, List<Long> eids, int fightValue){
		if(target != null){
			if(isSourceWin){
				tryGetMineReward(mine, target, time, "MINE_LOSE", true);
				List<Long> removeList = null;
				if(notifyPlayers.containsKey(target.getId())){
					removeList = target.getMines().getEidsByMineId(mine.getId());
				}
				target.getMines().unTakeMine(mine.getId());
				if(removeList != null){
					target.send(new CustomSync(SyncPush.SYNC_EMPLOYEE_UNTAKE, new RemovedEids(removeList)));
				}
				if(source != null){
					source.getMineReports().addNewMineReport(target, mine, MineReportManager.REPORT_TYPE_ATK_WIN, time);
					target.getMineReports().addNewMineReport(source, mine, MineReportManager.REPORT_TYPE_DEF_LOSE, time);
				}
			}else{
				if(source != null){
					source.getMineReports().addNewMineReport(target, mine, MineReportManager.REPORT_TYPE_ATK_LOSE, time);
					target.getMineReports().addNewMineReport(source, mine, MineReportManager.REPORT_TYPE_DEF_WIN, time);
				}
			}
		}
		if(source != null){
			if(isSourceWin){
				mine.end(source, time, fightValue);
				source.getMines().takeMine(mine.getId(), eids);
				mineQueue.put(mine);
			}else{
				if(target == null){
					mine.end(null, 0, 0);
					mineQueue.remove(mine);
				}
			}
		}
		if(isSourceWin){
			for (Player player : notifyPlayers.values()) {
				player.send(new MineChangePush(mine));
				//LOG.info("[MINEPUSH]PID[{}]NAME[{}]", player.getId(), player.getName());
			}
		}
	}
	
	/**
	 * 领取奖励
	 * @param mine
	 * @param player
	 * @param timeNow
	 * @param forceMail
	 */
	public int tryGetMineReward(Mine mine, Player player, long timeNow, String cause, boolean forceMail){
		long endCalcTime = mine.getTakeTime() + (mine.getExtendCount()) * TAKE_MAX_DURATION;
		long lastTakeTime = mine.getLastTakeTime();
		if(endCalcTime > timeNow){
			endCalcTime = timeNow;
		}
		int durationCount = 0;
		if(lastTakeTime > 0 && endCalcTime > lastTakeTime){
			durationCount = (int) ((endCalcTime - lastTakeTime) / GET_MONEY_DURATION);
			if(durationCount > 0){
				MineTypeTemplate temp = mine.getTypeTemp();
				if(forceMail || !player.isOnline()){
					MailService ms = App.getApp().getServiceManager().get(MailService.class);
					MailAttach attach = new MailAttach(temp.getProduceGold() * durationCount, 0);
					for(int i = 0;i < temp.getProduceItem().length; i++){
						attach.addItem(temp.getProduceItem()[i], temp.getProduceItemCount()[i] * durationCount);
					}
					ms.sendSystemMail(player.getId(), "探索奖励",  MessageFormat.format("总裁大人，您的{0}占领收益已发至邮箱，本次占领奖励如下", mine.getTypeTemp().getName()), attach);
//					player.getMineReports().addNewMineReport(null, mine, MineReportManager.REPORT_TYPE_MINE_UNTAKE, timeNow);
				}else{
					if(temp.getProduceGold() > 0){
						player.addGold(temp.getProduceGold() * durationCount, cause);
					}
					for(int i = 0;i < temp.getProduceItem().length; i++){
						player.getBag().addItemWithoutException(temp.getProduceItem()[i], temp.getProduceItemCount()[i] * durationCount, cause); 
					}
				}
			}
		}
		LOG.info("[MINE_GET]ID[{}]MAP[{}]POS[{}]TYPE[{}]COUNT[{}]CAUSE[{}]", player.getId(), mine.getMap(), mine.getPos(), mine.getMineType(), durationCount, cause);
		return durationCount;
	}

	@Override
	public boolean update() {
		long now = TimeUpdater.getInstance().now();
		if(now > nextCalcTime){
			//每5分钟进一次
			nextCalcTime = now + CHECK_CD;
//			for(List<Mine> list : mines.values()){}
			mines.forEach(new BiConsumer<Integer, List<Mine>>() {
				@Override
				public void accept(Integer t, List<Mine> list) {
					for(int i = 0, size = list.size(); i < size; i++){
						Mine mine = list.get(i);
						if(mine.getOwnerId() > 0){
							if(mine.getTakeTime() > 0L && mine.getTakeTime() + mine.getExtendCount() * TAKE_MAX_DURATION <= now){
//								App.getApp().getThreadPool().execute(new Runnable() {
//									@Override
//									public void run() {
//									}  
//								});
								Player player = App.getApp().getServiceManager().get(PlayerCacheService.class).find(mine.getOwnerId());
								if(player != null){
									tryGetMineReward(mine, player, now, "MINE_UPDATE", true);
									List<Long> removeList = null;
									if(notifyPlayers.containsKey(player.getId())){
										removeList = player.getMines().getEidsByMineId(mine.getId());
									}
									player.getMines().unTakeMine(mine.getId());
									if(removeList != null){
										player.send(new CustomSync(SyncPush.SYNC_EMPLOYEE_UNTAKE, new RemovedEids(removeList)));
									}
									mine.end(null, 0, 0);
									mineQueue.remove(mine);
									for (Player ply : notifyPlayers.values()) {
										ply.send(new MineChangePush(mine));
										//LOG.info("[MINEPUSH]PID[{}]NAME[{}]", player.getId(), player.getName());
									}
								}
							}
						}
					}
				}
			});
		}else if(now > nextQueueCheckTime){
			nextQueueCheckTime = now + QUEUECHECK_CD;
			Mine mine = mineQueue.peek();
			if(mine != null){
				if(mine.getOwnerId() > 0){
					if(mine.getTakeTime() > 0L && mine.getTakeTime() + mine.getExtendCount() * TAKE_MAX_DURATION <= now){
						Player player = App.getApp().getServiceManager().get(PlayerCacheService.class).find(mine.getOwnerId());
						if(player != null){
							tryGetMineReward(mine, player, now, "MINE_UPDATE", true);
							List<Long> removeList = null;
							if(notifyPlayers.containsKey(player.getId())){
								removeList = player.getMines().getEidsByMineId(mine.getId());
							}
							player.getMines().unTakeMine(mine.getId());
							if(removeList != null){
								player.send(new CustomSync(SyncPush.SYNC_EMPLOYEE_UNTAKE, new RemovedEids(removeList)));
							}
							mine.end(null, 0, 0);
							mineQueue.remove(mine);
							for (Player ply : notifyPlayers.values()) {
								ply.send(new MineChangePush(mine));
								//LOG.info("[MINEPUSH]PID[{}]NAME[{}]", player.getId(), player.getName());
							}
						}
					}
				}
			}
		}
		return true;
	}
}
